<?php

namespace backend\models\c2\entity;

use backend\models\c2\query\BeUserQuery;
use common\helpers\Password;
use common\models\c2\entity\AccessToken;
use common\models\c2\statics\AccessTokenType;
use common\models\c2\statics\EntityModelStatus;
use common\rest\models\c2\statics\OperationResult;
use kzo\user\Finder;
use kzo\user\Mailer;
use kzo\user\Module;
use kzo\user\traits\ModuleTrait;
use Yii;
use yii\base\Application;
use yii\base\NotSupportedException;
use yii\db\Query;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\helpers\ArrayHelper;
use yii\web\IdentityInterface;
use yii\web\UnauthorizedHttpException;

/**
 * This is the model class for table "{{%be_user}}".
 *
 * @property string $id
 * @property integer $type
 * @property string $attributeset_id
 * @property string $username
 * @property string $email
 * @property string $mobile_number;
 * @property string $password_hash
 * @property string $auth_key
 * @property string $confirmed_at
 * @property string $unconfirmed_email
 * @property string $blocked_at
 * @property string $registration_ip
 * @property integer $flags
 * @property string $last_login_at
 * @property string $last_login_ip
 * @property string $open_id
 * @property string $created_by
 * @property string $updated_by
 * @property integer $status
 * @property integer $position
 * @property string $created_at
 * @property string $updated_at
 */
class BeUser extends ActiveRecord implements IdentityInterface
{

    // use ModuleTrait;

    public $access_token;

    const BEFORE_CREATE = 'beforeCreate';
    const AFTER_CREATE = 'afterCreate';
    const BEFORE_REGISTER = 'beforeRegister';
    const AFTER_REGISTER = 'afterRegister';
    const BEFORE_CONFIRM = 'beforeConfirm';
    const AFTER_CONFIRM = 'afterConfirm';

    /** @var string Plain password. Used for model validation. */
    public $password;

    /** @var BeUserProfile|null */
    private $_profile;

    /** @var string Default username regexp */
    public static $usernameRegexp = '/^[-a-zA-Z0-9_\.@]+$/';

    /**
     * @return bool Whether the user is blocked or not.
     */
    public function getIsBlocked()
    {
        return $this->blocked_at != null;
    }

    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%be_user}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            // username rules
            'usernameTrim' => ['username', 'trim'],
            'usernameRequired' => ['username', 'required', 'on' => ['register', 'create', 'connect', 'update']],
            'usernameMatch' => ['username', 'match', 'pattern' => static::$usernameRegexp],
            'usernameLength' => ['username', 'string', 'min' => 3, 'max' => 255],
            'usernameUnique' => [
                'username',
                'unique',
                'message' => \Yii::t('app.c2', 'This username has already been taken')
            ],

            'mobileNumberTrim' => ['mobile_number', 'trim'],
            'mobileNumberRequired' => ['username', 'required', 'on' => ['register', 'create', 'connect', 'update']],
            'mobileNumberLength' => ['mobile_number', 'string', 'min' => 3, 'max' => 255],

            // email rules
            'emailTrim' => ['email', 'trim'],
            'emailRequired' => ['email', 'required', 'on' => ['register', 'connect', 'create', 'update']],
            'emailPattern' => ['email', 'email'],
            'emailLength' => ['email', 'string', 'max' => 255],
            'emailUnique' => [
                'email',
                'unique',
                'message' => \Yii::t('app.c2', 'This email address has already been taken')
            ],

            // password rules
            'passwordRequired' => ['password', 'required', 'on' => ['register']],
            'passwordLength' => ['password', 'string', 'min' => 6, 'max' => 72, 'on' => ['register', 'create']],
        ];
    }

    public function behaviors()
    {
        return [
            [
                'class' => TimestampBehavior::className(),
                'attributes' => [
                    ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
                    ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
                ],
                'value' => function () {
                    return date('Y-m-d H:i:s');
                },
            ],
        ];
    }

    /** @inheritdoc */
    public function scenarios()
    {
        $scenarios = parent::scenarios();
        return ArrayHelper::merge($scenarios, [
            'register' => ['username', 'email', 'password', 'mobile_number'],
            'connect' => ['username', 'email'],
            'create' => ['username', 'email', 'password', 'mobile_number'],
            'update' => ['username', 'email', 'password', 'mobile_number'],
            'settings' => ['username', 'email', 'password', 'mobile_number'],
        ]);
    }

    /**
     * @return bool
     * @throws \yii\base\Exception
     */
    public function block()
    {
        return (bool)$this->updateAttributes([
            'blocked_at' => date('Y-m-d H:i:s'),
            'auth_key' => \Yii::$app->security->generateRandomString(),
        ]);
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => Yii::t('app.c2', 'ID'),
            'type' => Yii::t('app.c2', 'Type'),
            'attributeset_id' => Yii::t('app.c2', 'Attributeset ID'),
            'username' => Yii::t('app.c2', 'Username'),
            'email' => Yii::t('app.c2', 'Email'),
            'password' => Yii::t('app.c2', 'Password'),
            'password_hash' => Yii::t('app.c2', 'Password Hash'),
            'auth_key' => Yii::t('app.c2', 'Auth Key'),
            'confirmed_at' => Yii::t('app.c2', 'Confirmed At'),
            'unconfirmed_email' => Yii::t('app.c2', 'Unconfirmed Email'),
            'blocked_at' => Yii::t('app.c2', 'Blocked At'),
            'registration_ip' => Yii::t('app.c2', 'Registration Ip'),
            'flags' => Yii::t('app.c2', 'Flags'),
            'last_login_at' => Yii::t('app.c2', 'Last Login At'),
            'last_login_ip' => Yii::t('app.c2', 'Last Login Ip'),
            'open_id' => Yii::t('app.c2', 'Open ID'),
            'created_by' => Yii::t('app.c2', 'Created By'),
            'updated_by' => Yii::t('app.c2', 'Updated By'),
            'status' => Yii::t('app.c2', 'Status'),
            'position' => Yii::t('app.c2', 'Position'),
            'created_at' => Yii::t('app.c2', 'Created At'),
            'updated_at' => Yii::t('app.c2', 'Updated At'),
            'mobile_number' => Yii::t('app.c2', 'Mobile Number')
        ];
    }

    /** @inheritdoc */
    public function validateAuthKey($authKey)
    {
        return $this->getAttribute('auth_key') === $authKey;
    }

    /**
     * Resets password.
     *
     * @param string $password
     *
     * @return bool
     * @throws \yii\base\Exception
     */
    public function resetPassword($password)
    {
        return (bool)$this->updateAttributes(['password_hash' => Password::hash($password)]);
    }

    /**
     * @inheritdoc
     * @return BeUserQuery the active query used by this AR class.
     */
    public static function find()
    {
        return new BeUserQuery(get_called_class());
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getProfile()
    {
        return $this->hasOne(BeUserProfile::className(), ['user_id' => 'id'])->from(BeUserProfile::tableName() . ' AS profile');
    }

    /**
     * @param BeUserProfile $profile
     */
    public function setProfile(BeUserProfile $profile)
    {
        $this->_profile = $profile;
    }

    /** @inheritdoc */
    public function getAuthKey()
    {
        return $this->getAttribute('auth_key');
    }

    /**
     * return a format data array for select2 ajax response
     * @param $keyField
     * @param $valField
     * @param $condition
     * @return array
     */
    public static function getOptionsListCallable($keyField, $valField, $condition = '', $params = [])
    {
        $params = ArrayHelper::merge(['limit' => 10], $params);
        $class = static::className();
        $items = [];
        $models = $class::find()->andWhere($condition)->limit($params['limit'])->all();
        foreach ($models as $model) {
            $items[] = [
                $keyField => $model->$keyField,
                $valField => $model->$valField,
                'text' => $model->$valField,
            ];
        }
        return $items;
    }

    public function getFullName()
    {
        if (!isset($this->_data['fullname'])) {
            $this->_data['fullname'] = is_null($this->profile) ? "" : $this->profile->getMemberName();
        }
        return $this->_data['fullname'];
    }

    public function beforeDelete()
    {
        if (Yii::$app->authManager->revokeAll($this->id)) {
            $this->unlinkAll('profile', true);
        }
        return parent::beforeDelete();
    }

    /**
     * UnBlocks the user by setting 'blocked_at' field to null.
     */
    public function unblock()
    {
        return (bool)$this->updateAttributes(['blocked_at' => null]);
    }


    /** @inheritdoc
     * @throws \yii\base\Exception
     */
    public function beforeSave($insert)
    {
        if ($insert) {
            $this->setAttribute('auth_key', \Yii::$app->security->generateRandomString());
            if (\Yii::$app instanceof Application) {
                $this->setAttribute('registration_ip', \Yii::$app->request->userIP);
            }
        }

        if (!empty($this->password)) {
            $this->setAttribute('password_hash', Password::hash($this->password));
        }

        return parent::beforeSave($insert);
    }

    /** @inheritdoc
     * @throws \yii\base\InvalidConfigException
     */
    public function afterSave($insert, $changedAttributes)
    {
        parent::afterSave($insert, $changedAttributes);
        if ($insert) {
            if ($this->_profile == null) {
                $this->_profile = \Yii::createObject(BeUserProfile::className());
            }
            $this->_profile->link('user', $this);
        }
    }


    /** @inheritdoc
     * @throws UnauthorizedHttpException
     * @throws \yii\base\Exception
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        $token = AccessToken::findOne(['access_token' => $token]);
        if (strtotime($token->expired_at) < time()) {
            throw new UnauthorizedHttpException(Yii::t("app.c2", 'Token expired.'), OperationResult::ERROR_901);
        }
        $user = self::find()->where(['id' => $token->user_id])->active()->one();
        if (!$user->getIsBlocked()) {
            $user->updateTokenExpired($token->access_token);
            return $user;
        }
        return false;
    }

    public static function findByUsername($username)
    {
        return self::find()->where(['username' => $username])->active()->one();
    }

    public function validatePassword($password)
    {
        return Yii::$app->security->validatePassword($password, $this->password_hash);
    }

    /**
     * @return bool
     * @throws \yii\base\Exception
     */
    public function login()
    {
        $model = $this->getAccessToken()->andFilterWhere(['type' => AccessTokenType::TYPE_BE_USER])->one();
        if (empty($model)) {
            $model = new AccessToken();
            $model->setAttributes([
                'type' => AccessTokenType::TYPE_BE_USER,
                'user_id' => $this->id,
                'access_token' => Yii::$app->security->generateRandomString(),
                'expired_at' => date('Y-m-d H:i:s', time() + AccessToken::EXPIRED_TIME),
                'equipment_id' => '',
                'status' => EntityModelStatus::STATUS_ACTIVE,
            ]);
            if (!$model->save()) {
                return false;
            }
        } else {
            $this->updateTokenExpired(Yii::$app->security->generateRandomString());
        }
        return true;
    }

    public function getAccessToken()
    {
        return $this->hasOne(AccessToken::className(), ['user_id' => 'id']);
    }

    public function getPermissionMenus()
    {
        return [
            'user' => $this,
            'token' => $this->accessToken->access_token
        ];
    }

    /**
     * @param $token
     */
    public function updateTokenExpired($token)
    {
        $model = $this->getAccessToken()->andFilterWhere(['type' => AccessTokenType::TYPE_BE_USER])->one();
        $model->updateAttributes([
            'access_token' => $token,
            'expired_at' => date('Y-m-d H:i:s', time() + AccessToken::EXPIRED_TIME)
        ]);
    }

    /**
     * @inheritDoc
     */
    public static function findIdentity($id)
    {
        // TODO: Implement findIdentity() method.
    }

    /**
     * @inheritDoc
     */
    public function getId()
    {
        // TODO: Implement getId() method.
    }
}
